require("chromedriver");
const { Builder, By, until } = require("selenium-webdriver");
const Element = require("./Element");
const fs = require("fs");
const path = require("path");

class Browser {
    constructor(timeout = 15000) {
        this.webDriver = this.createWebDriver();
        this.config = {
            timeout,
            screenshot: {
                isEnabled: false,
                pathname: null
            }
        };
    }

    createWebDriver() {
        return new Builder().forBrowser("chrome").build();
    }

    async get(url) {
        await this.webDriver.get(url);
    }

    async quit() {
        await this.webDriver.close();
        await this.webDriver.quit()
    }

    async refresh() {
        await this.webDriver.navigate().refresh();
    }

    async maximize() {
        await this.webDriver.manage().window().maximize();
    }

    async findElement(xpath) {
        await this.webDriver.wait(until.elementLocated(By.xpath(xpath)), this.config.timeout);
        let webElement = await this.webDriver.findElement(By.xpath(xpath));
        return new Element(webElement, this);
    }

    async findElements(xpath) {
        await this.webDriver.wait(until.elementLocated(By.xpath(xpath)), this.config.timeout);
        let webElements = await this.webDriver.findElements(By.xpath(xpath));
        return webElements.map(webElement => new Element(webElement, this));
    }

    async moveMouseTo(xpath) {
        await this.webDriver.wait(until.elementLocated(By.xpath(xpath)), this.config.timeout);
        let actions = this.webDriver.actions();
        actions.move({ origin: await this.webDriver.findElement(By.xpath(xpath)) });
        await actions.perform();
    }

    async sleep(ms) {
        await this.webDriver.sleep(ms);
    }

    async waitUntilTitleContains(substr) {
        await this.webDriver.wait(until.titleContains(substr), this.config.timeout);
    }

    async waitUntilUrlContains(substrUrl) {
        await this.webDriver.wait(until.urlContains(substrUrl), this.config.timeout);
    }

    async waitUntilElementBeVisible(xpath) {
        let count = 50;
        let timeout = this.config.timeout / count;
        while (count > 0) {
            await this.webDriver.wait(until.elementLocated(By.xpath(xpath)), this.config.timeout);
            let webElement = await this.webDriver.findElement(By.xpath(xpath));
            if (await webElement.isDisplayed()) {
                return;
            }
            await this.webDriver.sleep(timeout);
            count--;
        }
        throw new Error(`${xpath}没有出现`);
    }

    async  waitUntilElementBeNotVisible(xpath) {
        try {
            const element = await this.webDriver.findElement(By.xpath(xpath));
            await this.webDriver.wait(until.elementIsNotVisible(element), this.config.timeout);
        } catch (error) {

        }
    }

    async isDisplayed(xpath, timeout) {
        try {
            await this.webDriver.wait(until.elementLocated(By.xpath(xpath)), timeout);
            let webElements = await this.webDriver.findElements(By.xpath(xpath));
            for (let i = 0; i < webElements.length; i++) {
                let webElement = webElements[i];
                if (await webElement.isDisplayed()) {
                    return true;
                }
            }
        } catch (error) {

        }
        return false;
    }

    async getCurrentUrl() {
        return await this.webDriver.getCurrentUrl();
    }

    async executeScript(script, ...args) {
        return await this.webDriver.executeScript(script, ...args);
    }

    async isElementLocated(xpath) {
        try {
            await this.webDriver.findElement(By.xpath(xpath));
            return true;
        } catch (error) {
            return false;
        }
    }

    async switchToFrame(frameXpath) {
        if (frameXpath) {
            let frame = await this.findElement(frameXpath);
            await this.webDriver.switchTo().frame(frame.webElement);
        }
        else {
            await this.webDriver.switchTo().frame(null);
        }
    }

    async switchToWindow(index = 0) {
        let handles = await this.webDriver.getAllWindowHandles();
        let handle = handles[index];
        await this.webDriver.switchTo().window(handle);
    }

    takeScreenshot(pathname = null, filename = null) {
        pathname = pathname || (this.config.screenshot.isEnabled && this.config.screenshot.pathname ? this.config.screenshot.pathname : null) || `${process.cwd()}${path.sep}screenshot`
        filename = filename || `${new Date().valueOf()}`;
        filename = path.extname(filename) === "" ? `${filename}.png` : filename;
        Browser._mkdir(pathname);
        this.webDriver.takeScreenshot().then(base64 => {
            fs.writeFile(`${pathname}${path.sep}${filename}`, base64, "base64", error => {
                if (error) {
                    console.log(error);
                }
            });
        });
    }

    static _mkdir(pathname = "") {
        const arr = pathname.split(/\/|\\/);
        pathname = "";
        for (let i = 0; i < arr.length; i++) {
            pathname = i === 0 ? arr[0] : pathname + path.sep + arr[i]
            if (!fs.existsSync(pathname)) {
                fs.mkdirSync(pathname);
            }
        }
    }

}

module.exports = Browser;

